package http

import (
	"context"
	"errors"
	"fmt"
	"gitee.com/llakcs/agile-go/log"
	"gitee.com/llakcs/agile-go/registry"
	"gitee.com/llakcs/agile-go/selector"
	"gitee.com/llakcs/agile-go/transport"
	"gitee.com/llakcs/agile-go/transport/target"
	"gitee.com/llakcs/agile-go/utils/endpoint"
	"time"
)

type Resolver struct {
	Target   *target.Target
	watcher  registry.Watcher
	Selector selector.Selector
	Insecure bool
}

func NewResolver(ctx context.Context, discovery registry.Discovery, target *target.Target, block, insecure bool, selector selector.Selector) (*Resolver, error) {
	//获取服务名
	watcher, err := discovery.Watch(ctx, target.Endpoint)
	if err != nil {
		return nil, err
	}
	r := &Resolver{
		Target:   target,
		watcher:  watcher,
		Selector: selector,
		Insecure: insecure,
	}
	if block {
		done := make(chan error, 1)
		go func() {
			for {
				services, err := watcher.Next()
				if err != nil {
					done <- err
					return
				}
				if r.update(services) {
					done <- nil
					return
				}
			}
		}()
		select {
		case err := <-done:
			if err != nil {
				stopErr := watcher.Stop()
				if stopErr != nil {
					log.Errorf("failed to http client watch stop: %v, error: %+v", target, stopErr)
				}
				return nil, err
			}
		case <-ctx.Done():
			log.Errorf("http client watch service %v reaching context deadline!", target)
			stopErr := watcher.Stop()
			if stopErr != nil {
				log.Errorf("failed to http client watch stop: %v, error: %+v", target, stopErr)
			}
			return nil, ctx.Err()
		}
	}
	go func() {
		for {
			services, err := watcher.Next()
			if err != nil {
				if errors.Is(err, context.Canceled) {
					return
				}
				log.Errorf("http client watch service %v got unexpected error:=%v", target, err)
				time.Sleep(time.Second)
				continue
			}
			r.update(services)
		}
	}()
	return r, nil
}

func (r *Resolver) update(services []*registry.ServiceInstance) bool {
	nodes := make([]selector.Node, 0, len(services))
	fmt.Println("###services:", services)
	for _, ins := range services {
		ept, err := endpoint.ParseEndpoint(ins.Endpoints, endpoint.Scheme(transport.KindHTTP.String(), !r.Insecure))
		if err != nil {
			log.Errorf("Failed to parse (%v) discovery endpoint: %v error %v", r.Target, ins.Endpoints, err)
			continue
		}
		if ept == "" {
			continue
		}
		nodes = append(nodes, selector.NewNode(ept, ins))
	}

	if len(nodes) == 0 {
		log.Warnf("[http resolver]Zero endpoint found,refused to write,set: %s ins: %v", r.Target.Endpoint, nodes)
		return false
	}
	r.Selector.Add(nodes...)
	return true
}

func (r *Resolver) Close() error {
	return r.watcher.Stop()
}
